#include <g-bios.h>
#include <net/tftp.h>
#include <core/getopt.h>
#include <flash/part.h>
#include <arch/arm.h>



struct TftpPacket
{
	UINT16 wOpCode;
	union
	{
		UINT16 wBlkNum;
		UINT16 wErrNum;
	};
	BYTE pbData[0];
} __PACKED__;


static int TftpMakeRRQ(BYTE *pbBuff, const char *pchFileName)
{
	int len;

	*(UINT16 *)pbBuff = TFTP_RRQ;
	len = 2;

	strcpy(pbBuff + len, pchFileName);
	len += strlen(pchFileName);

	pbBuff[len] = '\0';
	len += 1;

	strcpy(pbBuff + len, TFTP_MODE_OCTET);
	len += strlen(TFTP_MODE_OCTET);

	pbBuff[len] = '\0';
	len += 1;

	return len;
}


static void TftpSendAck(const int fd, const UINT16 nBlkNum)
{
	struct TftpPacket tftpPkt;

	tftpPkt.wOpCode = TFTP_ACK;
	tftpPkt.wBlkNum = CPU_TO_BE16(nBlkNum);

	send(fd, &tftpPkt, TFTP_HDR_LEN);
}


// fixme
int GuNetTftpGetFile(struct TftpOpt *opt)
{
	int    ret;
	int    sockfd;
	BYTE   buf[TFTP_BUF_LEN];
	BYTE   szSvrIp[IPV4_STR_LEN];
	ULONG  nPktLen, nLoadLen;
	UINT16 wCurBlkNum;
	BYTE  *pbBufPtr;
	UINT32 ulFlshAddr;
	UINT32 ulCase, nip;
	struct TftpPacket *pTftpPkt;
	struct sockaddr   *skaddr;


	if (GuIpToStr(szSvrIp, opt->dwSvrIP) < 0)
	{
		printf("Error: Server IP!\n");
		return -EINVAL;
	}

	pTftpPkt = (struct TftpPacket *)buf;

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);

	if (sockfd <= 0)
	{
		printf("%s(): error @ line %d!\n", __FUNCTION__, __LINE__);
		return -EIO;
	}

	GuStrToIp((BYTE *)&nip, szSvrIp);

	skaddr = getaddr(nip);

	if (NULL == skaddr)
    {
		skaddr = gethostaddr(szSvrIp);
		if (NULL == skaddr)
		{
			printf("%s(): addr error!\n", __FUNCTION__);
			return -EINVAL;
		}
	}

	skaddr->wSrcPort = CPU_TO_BE16(1234); // fixme: NetPortAlloc
	skaddr->wDesPort = CPU_TO_BE16(STD_PORT_TFTP);

	ret = connect(sockfd, skaddr, sizeof(struct sockaddr));

	nPktLen = TftpMakeRRQ((BYTE *)pTftpPkt, opt->szFileName);

	send(sockfd, pTftpPkt, nPktLen);

	pbBufPtr   = opt->pLoadAddr;
	nLoadLen   = 0;
	wCurBlkNum = 1;

	do
	{
		nPktLen = recv(sockfd, pTftpPkt, TFTP_BUF_LEN);

		nPktLen -= TFTP_HDR_LEN;

		if (nPktLen > TFTP_PKT_LEN)
			nPktLen = TFTP_PKT_LEN;

		switch (pTftpPkt->wOpCode)
		{
		case TFTP_DAT:
			if (BE16_TO_CPU(pTftpPkt->wBlkNum) == wCurBlkNum)
			{
				if (NULL != pbBufPtr)
				{
					memcpy(pbBufPtr, pTftpPkt->pbData, nPktLen);
					pbBufPtr += nPktLen;
				}

				if (NULL != opt->pPart)
				{
					UINT32 psr;

					// LockIrqPsr(psr);
					ret = GuPartWrite(opt->pPart, pTftpPkt->pbData, nPktLen);
					// UnlockIrqPsr(psr);
					if (ret < 0)
						goto L1;
				}

				nLoadLen += nPktLen;

				TftpSendAck(sockfd, wCurBlkNum);
				wCurBlkNum++;

#ifdef TFTP_VERBOSE
				if ((nLoadLen & 0x3fff) == 0)
				{
					printf("\r%s => %s: ", szSvrIp, opt->szFileName);

					if (nLoadLen >> 20)
						printf("%02dM%03dK ", nLoadLen >> 20, (nLoadLen >> 10) & 0x3ff);
					else
						printf("%03dK ", (nLoadLen >> 10) & 0x3ff);
				}
#endif
			}
			else
			{
#ifdef TFTP_DEBUG
				printf("\t%s(): LOST Packet = 0x%x (0x%x).\r", 
					__FUNCTION__, wCurBlkNum, BE16_TO_CPU(pTftpPkt->wBlkNum));
#endif
				TftpSendAck(sockfd, wCurBlkNum - 1);
			}

			break;

		case TFTP_ERR:
			printf("%s(): %s (Error Num = %d)\n",
				__FUNCTION__, pTftpPkt->pbData,	BE16_TO_CPU(pTftpPkt->wErrNum));

			ret = -EIO;
			goto L1;

		default:
			printf("%s(): Unsupported opcode 0x%02x! (CurBlkNum = %d)\n",
				__FUNCTION__, BE16_TO_CPU(pTftpPkt->wOpCode), wCurBlkNum);

			ret = -EIO;
			goto L1;
		}		
	}while (TFTP_PKT_LEN == nPktLen);

#ifdef TFTP_VERBOSE
	printf("\n");
#endif

L1:
	close(sockfd);

	return nLoadLen;
}

